﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Runtime.InteropServices;

using SsrsRenderStudio.Shared;
using SsrsRenderStudio.Renderer.Support;
using SsrsRenderStudio.KnownTypesAndModels;
using SsrsRenderStudio.Shared.HostServices;
using SsrsRenderStudio.Shared.Windows32API;

namespace SsrsRenderStudio.Renderer.Output.Version2010
{
    public sealed class PrinterOutput2 : MetafileOutput
    {
        private Func<DeviceContextCachingParameters, DeviceContextCachingInformation> _getDeviceContextFunc;
        private PrinterInfo10 _printerInformation;

        /// <summary>
        /// Printer Output.
        /// </summary>
        /// <param name="metafiles">Array of metafiles to print.</param>
        /// <param name="destination">Path of printer.</param>
        /// <param name="spoolDirect">Print directly through Windows spooler.  Default is FALSE.</param>
        public PrinterOutput2(byte[][] renderReport, PrinterInfo10 printerInformation, Func<DeviceContextCachingParameters, DeviceContextCachingInformation> getDeviceContextFunc)
            : base(renderReport) 
        {
            _getDeviceContextFunc = getDeviceContextFunc;
            _printerInformation = printerInformation;
        }

        float _stockWidth = 0.0f;
        float _stockHeight = 0.0f;
        bool _isLandscape = false;
        bool _isFlip = false;
        float _marginLeft = 0.2f;
        float _marginRight = 0.2f;
        float _marginTop = 0.2f;
        float _marginBottom = 0.2f;

        private Metafile _currentMetafile = null;
        private int _currentPrintingPage;
        private int _lastPrintingPage;
        private int _numberOfPages;

        public void SetOptions(dynamic options, bool isFlipped = false)
        {
            _isFlip = isFlipped;

            if (options.GetType() == typeof(PageSettings10))
            {
                PageSettings10 options10 = options as PageSettings10;

                _isLandscape = false;
                if (options10.Width > options10.Height) _isLandscape = true;

                float multiplier = 0.0394f;
                if (!options10.IsMetric) multiplier = 1f;

                //TODO might have to swap these
                _stockHeight = (float)(options10.Height * multiplier);
                _stockWidth = (float)(options10.Width * multiplier);

                _marginLeft = (float)(options10.MarginLeft * multiplier);
                _marginRight = (float)(options10.MarginRight * multiplier);
                _marginTop = (float)(options10.MarginTop * multiplier);
                _marginBottom = (float)(options10.MarginBottom * multiplier);
            }

            if (options.GetType() == typeof(PrintOptions))
            {
                _stockHeight = options.StockHeight;
                _stockWidth = options.StockWidth;
                _isLandscape = options.IsLandscape;
            }
        }

        public override void Process()
        {
            Logger.StartSection("printer_process");
            PrintReportViaSpooler2();
            Logger.Log(EnumLoggingSeverity.DEBUGVERBOSE, "PrinterOutput: * Process() method " + Logger.StopSection("printer_process").TotalMilliseconds);
        }

        /// <summary>
        /// Sets current metafile variable to requested page.
        /// </summary>
        private bool MoveToPage(Int32 page)
        {
            _currentMetafile = _metafiles[page - 1];
            return true;
        }

        /// <summary>
        /// Print directly to spooler.
        /// </summary>
        private void PrintReportViaSpooler2()
        {
            _numberOfPages = _metafiles.Length;
            _currentPrintingPage = 1;
            _lastPrintingPage = _numberOfPages;

            IntPtr deviceContext;
            GDI.DOCINFO documentInfo;
            int hardMarginLeft;
            int hardMarginTop;

            documentInfo = new GDI.DOCINFO();
            documentInfo.fwType = 0;
            documentInfo.lpszDatatype = null;
            documentInfo.lpszDocName = "SsrsRenderStudioPrintJob";
            documentInfo.lpszOutput = null;
            documentInfo.cbSize = Marshal.SizeOf(documentInfo);

            Logger.StartSection("printer_createdc");
            DeviceContextCachingInformation cacheInfo = _getDeviceContextFunc(new DeviceContextCachingParameters { NetworkDestination = _printerInformation.NetworkDestination, IsCachingEnabled = _printerInformation.CacheDeviceContext, CacheTimeToLive = _printerInformation.DeviceContextTTL });
            deviceContext = cacheInfo.IsValid ? cacheInfo.DeviceContextHandle : IntPtr.Zero;
            //deviceContext = _getDeviceContextFunc(_destination);  //todo: device context handle gets stale and no prints come out
            //deviceContext = PrinterOutputPinvoke.CreateDC("WINSPOOL", _printerInformation.NetworkDestination.Normalize(), null, IntPtr.Zero);
            Logger.Log(EnumLoggingSeverity.DEBUGVERBOSE, "PrinterOutput: * Creating WINSPOOL device context " + Logger.StopSection("printer_createdc").TotalMilliseconds);

            if (deviceContext != IntPtr.Zero)
            {
                Logger.StartSection("printer_getdevicecaps");
                //todo: point of failure?
                hardMarginLeft = GDI.GetDeviceCaps(
                    deviceContext,
                    (int)GDI.DeviceCap.PHYSICALOFFSETX);
                hardMarginTop = GDI.GetDeviceCaps(
                    deviceContext,
                    (int)GDI.DeviceCap.PHYSICALOFFSETY);

                hardMarginLeft = (int)(hardMarginLeft * 100F / GDI.GetDeviceCaps(deviceContext, (int)GDI.DeviceCap.LOGPIXELSX));
                hardMarginTop = (int)(hardMarginTop * 100F / GDI.GetDeviceCaps(deviceContext, (int)GDI.DeviceCap.LOGPIXELSY));

                Logger.Log(EnumLoggingSeverity.DEBUGVERBOSE, "PrinterOutput: * Get device information " + Logger.StopSection("printer_getdevicecaps").TotalMilliseconds);

                Point[] points = new Point[3] { new Point(0, 0), 
                                            new Point(Convert.ToInt16(_stockWidth * 100), 0), 
                                            new Point(0, Convert.ToInt16(_stockHeight * 100)) };

                Logger.StartSection("printer_startdoc");
                int startdoc = GDI.StartDoc(deviceContext, ref documentInfo);
                Logger.Log(EnumLoggingSeverity.DEBUGVERBOSE, "PrinterOutput: * StartDoc " + Logger.StopSection("printer_startdoc").TotalMilliseconds);

                if (startdoc > 0)
                {
                    for (int i = 0; i < _numberOfPages ; ++i)
                    {
                        MoveToPage(_currentPrintingPage);
                            
                        using (var g = Graphics.FromHdcInternal(deviceContext))
                        {   
                            g.TranslateTransform(-hardMarginLeft, -hardMarginTop);
                            int startpage = GDI.StartPage(deviceContext);

                            if (startpage > 0)
                            {
                                float xTrans = 0f;
                                float yTrans = 0f;
                                float qRotate = 0f;

                                lock (this)
                                {
                                    if (_isLandscape)
                                    {
                                        if (_isFlip)
                                        {
                                            qRotate = 90f;
                                            yTrans = _stockHeight * -100;
                                        }
                                        else
                                        {
                                            qRotate = -90f;
                                            xTrans = _stockWidth * -100;
                                        }
                                    }
                                    else
                                    {
                                        if (_isFlip)
                                        {
                                            qRotate = 180;
                                            xTrans = _stockWidth * -100;
                                            yTrans = _stockHeight * -100;
                                        }
                                    }

                                    g.RotateTransform(qRotate);

                                    // move origin
                                    g.TranslateTransform(xTrans, yTrans);
                                    MetafilePainterInstance painter = new MetafilePainterInstance();
                                    painter.PaintImage(g, _currentMetafile, points);

                                    g.Flush();
                                }

                                _currentPrintingPage++;
                                int endpage = GDI.EndPage(deviceContext);

                                if (endpage <= 0)
                                {
                                    Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _printerInformation.NetworkDestination + " EndPage failed.");
                                }
                            }
                            else
                            {
                                Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _printerInformation.NetworkDestination + " StartPage failed.");
                            }
                        }
                    }

                    Logger.StartSection("printer_enddoc");
                    int enddoc = GDI.EndDoc(deviceContext);
                    Logger.Log(EnumLoggingSeverity.DEBUGVERBOSE, "PrinterOutput: * EndDoc " + Logger.StopSection("printer_enddoc").TotalMilliseconds);

                    if (enddoc <= 0)
                    {
                        Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _printerInformation.NetworkDestination + " EndDoc failed.");
                    }
                }
            }
            else
            {
                //todo : need to throw some errros for REX on this.
                Logger.Log(EnumLoggingSeverity.ALERT, "PrinterOutput: " + _printerInformation.NetworkDestination + " printer not available [Win32:" + Marshal.GetLastWin32Error().ToString() + "]");
            }

            /*
            if (deviceContext != IntPtr.Zero)
            {
                Logger.StartSection("printer_deletedc");
                PrinterOutputPinvoke.DeleteDC(deviceContext);
                Logger.Log(EnumLoggingSeverity.DEBUGVERBOSE, "PrinterOutput: * DeleteDc " + Logger.StopSection("printer_deletedc").TotalMilliseconds);
            }
            */
        }
    }
}
